package com.hue.spring;

import com.hue.spring.annotation.Autowired;
import com.hue.spring.annotation.Component;
import com.hue.spring.annotation.ComponentScan;
import com.hue.spring.annotation.Scope;
import com.hue.spring.aware.BeanNameAware;
import com.hue.spring.init.BeanPostProcessor;
import com.hue.spring.init.InitializingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/*
    Spring的应用程序类
 */
public class ApplicationContext {
    //配置类
    private Class configClass;

    //ioc容器
    private ConcurrentHashMap<String, Object> singleMap = new ConcurrentHashMap<>();

    //存储所有加载过的bean的定义
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //存储BeanPostProcessor的集合
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public ApplicationContext(Class configClass) {
        this.configClass = configClass;
        //解析配置类
        try {
            parseConfigClass(configClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //将所有单例bean创建出来并加入到ioc容器中
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            //获取bean的名字
            String beanName = entry.getKey();
            //获取beanDefinition
            BeanDefinition beanDefinition = entry.getValue();
            //如果是单例并且不是BeanPostProcessor的bean就创建
            if (beanDefinition.getScope().equals("single") &&
                    !BeanPostProcessor.class.isAssignableFrom(beanDefinition.getBeanClass())) {
                Object bean = createBean(beanName, beanDefinition);
                //添加到ioc中
                singleMap.put(beanName, bean);
            }
        }
    }

    //根据beanName获取一个bean
    public Object getBean(String name) {
        //在beanDefinitionMap中寻找这个bean的定义
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if (beanDefinition == null) {
            return null;
//            throw new RuntimeException("错误的创建了name为:" + name + "的bean");
        }
        //判断这个bean的作用域
        String scope = beanDefinition.getScope();
        Object o = null;
        if (scope.equals("single")) { //是单利bean，直接从ioc容器里面获取
            o = singleMap.get(name);
        } else { //是prototype
            o = createBean(name, beanDefinition);
        }
        return o;
    }

    //解析配置类
    public void parseConfigClass(Class configClass) throws Exception {
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //获取包扫描的路径
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value();
            path = path.replace(".", "/");
            //获取类加载器
            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL url = classLoader.getResource(path);
            //将url按utf-8解码
            URL url1 = new URL(URLDecoder.decode(url.toString(), "UTF-8"));
            File file = new File(url1.getFile());
            if (file.isDirectory()) {    //如果当前包是个目录
                //得到包下的所有文件
                File[] files = file.listFiles();
                for (File f : files) {
                    //得到文件绝对路径
                    String absPath = f.getPath();
                    //判断是否以.class结尾
                    if (absPath.endsWith(".class")) {
                        //得到.class文件的全限定类名
                        absPath = absPath.substring(absPath.indexOf("com"), absPath.indexOf(".class"));
                        absPath = absPath.replace("\\", ".");
                        //加载这个类
                        Class<?> aClass = Class.forName(absPath);
                        //如果这个类有@Component注解,表示这是个bean
                        if (aClass.isAnnotationPresent(Component.class)) {
                            //1.创建beanDefinition
                            //获取bean的名字
                            Component component = aClass.getAnnotation(Component.class);
                            String beanName = component.name();
                            if (beanName.equals("")) {    //用户没有指定name，默认为类名第一个字母小写
                                beanName = aClass.getName();
                                beanName = beanName.substring(beanName.lastIndexOf(".") + 1);
                                beanName = (beanName.charAt(0) + "").toLowerCase() + beanName.substring(1);
                            }
                            //创建beanDefinition
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setBeanClass(aClass);
                            //判断有没有@Scope注解,来设置beanDefinition的作用域
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                //获取scope的作用域
                                Scope scope = aClass.getAnnotation(Scope.class);
                                String value = scope.value();
                                if (value.equals("single")) { //指定为单例
                                    beanDefinition.setScope("single");
                                } else { //是prototype
                                    beanDefinition.setScope("prototype");
                                }
                            } else { //没有Scope注解，默认为单例
                                beanDefinition.setScope("single");
                            }
                            //将bean的定义加入到beanDefinitionMap中
                            beanDefinitionMap.put(beanName, beanDefinition);
                            //2.如果是一个BeanPostProcessor，提前将这个bean创建，并且加入到beanPostProcessors、ioc中
                            if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                                Object bean = createBean(beanName, beanDefinition);
                                beanPostProcessors.add((BeanPostProcessor) bean);
                                singleMap.put(beanName,bean);
                            }
                        }
                    }
                }
            }
        }
    }

    //根据beanDefinition创建一个bean
    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class aClass = beanDefinition.getBeanClass();
        Object o = null;
        try {
            //1.实例化
            o = aClass.newInstance();
            //2.依赖注入
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    //获取required的值
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    boolean required = autowired.required();
                    //使用getBean方法获取值
                    Object value = getBean(field.getName());
                    if (required && value == null) {    //如果必须赋值容器里面又没有
                        throw new RuntimeException("错误的创建了一个bean:" + field.getName());
                    }
                    field.setAccessible(true);
                    field.set(o, value);
                }
            }
            //3.Aware回调(为beanName属性赋值)
            if (o instanceof BeanNameAware) {
                //调用beanNameAware方法为beanName赋值
                ((BeanNameAware) o).beanNameAware(beanName);
            }
            //4.调用所有初始化之前的方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                beanPostProcessor.postProcessorBeforeInitialization(o, beanName);
            }
            //5.初始化
            if (o instanceof InitializingBean) {
                //调用初始化方法
                ((InitializingBean) o).afterPropertiesSet();
            }
            //6.初始化之后的方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                o = beanPostProcessor.postProcessorAfterInitialization(o, beanName);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }
}
